/*
=* T.Raynal - A.Aviles - F.Diallo
 * XML-Scenarios
 * PPA AL5-2
 * December 2012 
 */
package model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;

import common.Commons.ITEM;
import common.Commons.STATE;
import common.exception.EvaluationException;
import model.scenario.UnregisteredComponentException;
import model.serialization.XmlArray.ELEMENT;
import model.serialization.*;
import model.serialization.XmlArray.STRUCTURE;

import model.situation.Item;

// TODO: Auto-generated Javadoc
//extends Evaluator<Expression, String> 

/**
 * Represent an alternative within a Situation.
 */
@XmlElement(name = "CASE")
public class Case implements XmlSerializable, Serializable  {

	private static final long serialVersionUID = 1L;

	/**Alternative expressions*/
	@XmlArray(name ="EXPRESSIONS", hasContainer=false, structure=STRUCTURE.STACK, type=ELEMENT.EXPRESSION) 
	public Stack<Item> Expressions;
	
	/** Id of the alternative */
	@XmlAttribute(name = "lib")
	public String Label;
	
	/** Yield of the alternative - Id of the next Situation  to be evaluated.*/
	@XmlAttribute(name = "fork")
	public String Yield;
	
	private boolean _result;
	
	private STATE _state;
	
	/**
	 * Instantiates a new case.
	 */
	public Case()
	{
		Expressions = new Stack<>();
		_state = STATE.PENDING;
	}
	
	/**
	 * State of the alternative.
	 *
	 * @return the state
	 */
	public STATE state()
	{
		return _state;
	}
	
	/**
	 * Yield of the alternative - Id of the next Situation  to be evaluated.
	 *
	 * @return the string
	 */
	public String yield()
	{
		return Yield;
	}
	
	/**
	 * Load an expression.
	 *
	 * @param Expression
	 */
	public void put (Expression expression)
	{
		Item item = new Item(ITEM.EXPRESSION);
		item.storeItem((Object)expression);
		Expressions.push(item);
	}
	
	/**
	 * Load an operator.
	 *
	 * @param Operator.
	 */
	public void put (Object operator)
	{
		Item item = new Item(ITEM.OPERATOR);
		item.storeItem((Object)operator);
		Expressions.push(item);
	}

	/**
	 * Next element to be evaluated.
	 *
	 * @return Expression
	 * @throws EvaluationException
	 */
	public Expression next() throws EvaluationException {
		
		Expression evaluatee = null;
		
		if (Expressions.empty())
			throw new EvaluationException("Empty situation stack");
		
			for (int i = 0; i <Expressions.size();i++)
			{
				if (Expressions.elementAt(i).getType()==ITEM.EXPRESSION && 
					((Expression)Expressions.elementAt(i).releaseItem()).state()== STATE.PENDING)
					{
						evaluatee = (Expression)Expressions.elementAt(i).releaseItem();
						break;
					}
			}
			
			return evaluatee;
	}


	/**
	 * Reset the alternative.
	 */
	public void reset() {
		for (Item item : Expressions) {
			
			//item.setState(STATE.PENDING);
			
			if (item.getType()==ITEM.EXPRESSION)
			{
				((Expression)item.releaseItem()).reset();
			}
		}
		_result = false;
		_state = STATE.PENDING;
	}
	
	/**
	 * Evaluate the alternative.
	 *
	 * @return true, if successful
	 * @throws EvaluationException
	 */
	public boolean eval() throws EvaluationException
	{
		this._state = STATE.PENDING;
		this._result = false;

		if (Expressions.empty())
			throw new EvaluationException("Empty situation stack");
		
		if (null != next())
			throw new EvaluationException("One or more evalutatee unspecified");
		
		Stack<Item> expression = new Stack<>();
		
		for (Item item : new ArrayList<Item>(Expressions)) {
			expression.push(item);
		} 
		
		Stack<Result> results = new Stack<>();
		Stack<Item> temp = new Stack<>();
		Item currentItem =null;
		
		while (expression.size()>0  && (currentItem= expression.pop())!= null)
		{
			if (currentItem.getType()==ITEM.EXPRESSION)
			{
				temp.push(currentItem);
			}
			else
			{
				if (temp.size()==0)
				{	
					if (results.size()<2)
						throw new EvaluationException("Missing operand");
				
					Result operand1 = results.pop();
					Result operand2 = results.pop();
					Item operator = currentItem;
					Result result =operand1.eval((String)operator.releaseItem(),operand2);
					results.push(result);
					
				}
				else
				{
					Item operand = temp.pop();
					Item operator = currentItem;
					
					Result result =((Expression)operand.releaseItem()).eval((String)operator.releaseItem(), ((Expression)operand.releaseItem()).getEvaluatee());
					results.push(result);
				}
			}
		}
		
		if (results.size()!=1)
			throw new EvaluationException("Something went awfully wrong");
		
		
		
		_result =results.pop().result();
		
		this._state = STATE.PROCEEDED;
		return _result;
	}

	/**
	 * Get an iterator for the alternative's loaded expressions 
	 *
	 * @return Iterator
	 */
	public Iterator<Expression> iterator() {
			
		ArrayList<Expression> array = new ArrayList<>();
		
		for (Item item : Expressions) {
			if (item.getType()==ITEM.EXPRESSION)
				array.add((Expression)item.releaseItem());
		}
		
		return array.iterator();
	}

	/* (non-Javadoc)
	 * @see model.serialization.XmlSerializable#id()
	 */
	@Override
	public String id() {
		return Label;
	}

	/**
	 * Current evaluated expression.
	 *
	 * @return Expression
	 * @throws EvaluationException
	 */
	public Expression current() throws EvaluationException {
		return next();
	}

	/**
	 * Gets an loaded expression by name.
	 *
	 * @param Expression's id.
	 * @return Expression
	 * @throws UnregisteredComponentException
	 */
	public Expression get(String id) throws UnregisteredComponentException {
		
		Expression result = null;
		
		for (Item item : Expressions) {
			if ((item.getType()==ITEM.EXPRESSION) && ((Expression)item.releaseItem()).Label.equals(id))
			{
				result =(Expression)item.releaseItem();
				break;
			}
		}
		
		return result;
	}

}
